
// ===============================================================================================================
// -*- C++ -*-
//
// Billboard.cpp - Definition of a billboard class.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <Billboard.hpp>
#include <Common.hpp>

// =========================================================
// Billboard Class Implementation
// =========================================================

Billboard * Billboard::Create(const Sprite * anim, float distForAnimPlayback, float sizeFactor, const Vec3 & position)
{
	try {
		return (new Billboard(anim, distForAnimPlayback, sizeFactor, position));
	}
	catch (...) {
		return (0); // Return a null pointer on error.
	}
}

void Billboard::SetSprite(const Sprite * s)
{
	if (s != 0) // Only if 's' not null
	{
		if (sprite != 0)
		{
			sprite->Release();
		}

		sprite = s;
		sprite->AddRef();
	}
}

const Sprite * Billboard::GetSprite(void) const
{
	return (sprite);
}

void Billboard::SetDistForAnimPlayback(float dist)
{
	distForAnimToPlay = dist;
}

float Billboard::GetDistForAnimPlayback(void) const
{
	return (distForAnimToPlay);
}

unsigned long Billboard::AddRef(void) const
{
	return (++refCount);
}

unsigned long Billboard::Release(void) const
{
	if (--refCount == 0)
	{
		// Time to die...
		delete this;
		return (0);
	}

	return (refCount);
}

unsigned long Billboard::ReferenceCount(void) const
{
	return (refCount);
}

Billboard::Billboard(const Sprite * anim, float distForAnimPlayback, float sizeFactor, const Vec3 & position)
: sprite(anim), textureID(0), distForAnimToPlay(distForAnimPlayback), size(sizeFactor), pos(position)
{
	if (sprite)
	{
		sprite->AddRef();
	}

	// Adjust the position:
	pos.y = pos.y + size - 0.5f;

	// Create the base texture:
	glGenTextures(1, &textureID);
	glBindTexture(GL_TEXTURE_2D, textureID);

	// Setup texture filters:
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);

	const Image * img = sprite->GetCurrentFrame();
	MemoryBuffer * pixelBuf = img->Pixels();

	// Build the texture and generate mipmaps:
	if (GLEW_SGIS_generate_mipmap && img->IsPowerOfTwo())
	{
		// Hardware mipmap generation (Nice):
		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
		glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);

		glTexImage2D(GL_TEXTURE_2D, 0, img->Components(),
		img->Width(), img->Height(), 0, img->Format(), GL_UNSIGNED_BYTE, pixelBuf->GetBufferPointer());
	}
	else
	{
		// No hardware mipmap generation support, fall back to the good old gluBuild2DMipmaps() function:
		gluBuild2DMipmaps(GL_TEXTURE_2D, img->Components(),
		img->Width(), img->Height(), img->Format(), GL_UNSIGNED_BYTE, pixelBuf->GetBufferPointer());
	}

	pixelBuf->Release();
}

Billboard::~Billboard(void)
{
	if (sprite)
	{
		sprite->Release();
	}

	glDeleteTextures(1, &textureID);
}